﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../global/head.h"
#include "../macro/head.h"
#include "aio_helper.h"
#include "aio_impl.h"
#include "aio_udp.h"

namespace xos_aio
{
    aio_udp::aio_udp( aio_impl * pAioImpl ) : m_pAioImpl( pAioImpl )
    {
    }

    aio_udp::~aio_udp()
    {
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // m_bData[0] : true or false for result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[1] : local port
    // m_szStr[1] : local ip
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::udp_init( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;
        void * lpUserKey = lpMsg->get_void( 0, 0 );

        SOCKET hSocket = INVALID_SOCKET;
        aio_key * lpAioKey = 0;
        xos::i_socket * lpSocket = 0;

        if( 0 == nRet )
        {
            lpMsg->set_int( 0, i_aio::AIO_UDP_INIT_RET );
            lpMsg->set_bool( 0, false );
            lpMsg->set_void( 1, 0 );
        }

        if( 0 == nRet )
        {
            nRet = g_pSysData->m_pContainer->xos()->create( xos::i_xos::XOS_OBJ_SOCKET, ( void ** )&lpSocket );
        }

        if( 0 == nRet )
        {
            xos::xos_u16 usPort = ( xos::xos_u16 )lpMsg->get_int( 1, 0 );
            const char * pszIp = lpMsg->get_str( 1, 0, 0, 0 );
            nRet = lpSocket->udp_init( pszIp, usPort );
        }

        if( 0 == nRet )
        {
            nRet = lpSocket->set_async( true );
        }

        if( 0 == nRet )
        {
            xos::xos_u16 usPort = 0;
            char szIp[100] = { 0 };
            nRet = lpSocket->get_socket_addr( szIp, &usPort );
            if( 0 == nRet )
            {
                lpMsg->set_str( 1, szIp );
                lpMsg->set_int( 1, usPort );
            }
        }

        if( 0 == nRet )
        {
            lpAioKey = aio_key::get_item_from_pool( true );
            lpAioKey->init();
            lpAioKey->m_eState = aio_key::KEY_STATE_RUNNING;
            lpAioKey->m_lpUserKey = lpUserKey;
            lpAioKey->m_pSocket = lpSocket;
            hSocket = ( SOCKET )lpSocket->get_socket();
        }

        if( 0 == nRet )
        {
            HANDLE h = CreateIoCompletionPort( ( HANDLE )hSocket, m_pAioImpl->m_hIocp, ( ULONG_PTR )lpAioKey, 0 );
            if( h != m_pAioImpl->m_hIocp )
            {
                nRet = 1;
            }
        }

        if( 0 == nRet )
        {
            lpMsg->set_void( 1, lpAioKey );
            lpMsg->set_bool( 0, true );
            lpSocket = 0;
            lpAioKey = 0;
        }

        m_pAioImpl->notify( lpMsg );
        lpMsg = 0;

        if( lpSocket )
        {
            lpSocket->release();
            lpSocket = 0;
        }

        if( lpAioKey )
        {
            lpAioKey->m_pSocket = 0;
            lpAioKey->release();
            lpAioKey = 0;
        }

        return nRet;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[3] : recv len
    // m_lpBuf[0] : recv buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::udp_recv_ret( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        using namespace std;
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;
        aio_key * lpAioKey = ( aio_key * )lpMsg->get_void( 1, 0 );
        xos_box::i_buf * lpBuf = lpMsg->get_buf( 0, 0 );

         --lpAioKey->m_nRequestNum;

        if( ( 0 != nResult ) || ( 0 == dwBytes ) || !lpMsg->get_bool( 0, 0 ) )
        {
            nRet = 1;
        }

        if( 0 == nRet )
        {
            char ip[100] = { 0 };
            //lpMsg->set_str( 2, inet_ntoa( lpData->m_addr.sin_addr ) );
            lpMsg->set_str( 2, inet_ntop(AF_INET, (const void*)&lpData->m_addr.sin_addr, ip, sizeof(ip)));
            lpMsg->set_int( 2, ntohs( lpData->m_addr.sin_port ) );
            lpMsg->set_int( 3, dwBytes );
            lpBuf->set_len( dwBytes );
            lpMsg->set_bool( 0, true );
        }
        else
        {
            lpMsg->set_bool( 0, false );
        }

        m_pAioImpl->notify( lpMsg );

        return nRet;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[3] : recv len
    // m_lpBuf[0] : recv buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::udp_recv( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        using namespace std;
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;
        void * lpUserKey = lpMsg->get_void( 0, 0 );
        aio_key * lpAioKey = ( aio_key * )lpMsg->get_void( 1, 0 );
        xos::i_socket * lpSocket = lpAioKey->m_pSocket;
        SOCKET hSocket = ( SOCKET )lpSocket->get_socket();
        xos_box::i_buf * lpBuf = lpMsg->get_buf( 0, 0 );
        aio_data * lpRetData = 0;

        if( 0 == nRet )
        {
            lpMsg->set_int( 0, i_aio::AIO_UDP_RECV_RET );
            lpMsg->set_bool( 0, false );
            lpAioKey->m_nRequestNum++;
        }

        if( 0 == nRet )
        {
            lpRetData = aio_data::get_item_from_pool( true );
            lpRetData->init();
            lpRetData->m_nType = lpMsg->get_int( 0, 0 );
            lpRetData->m_lpMsgData = lpMsg;
        }

        if( 0 == nRet )
        {
            WSABUF * lpWsaBuf = lpBuf->get_wsa_buf( 0 );
            lpMsg->set_bool( 0, true );
            int ret = ::WSARecvFrom( hSocket, lpWsaBuf, 1, &lpRetData->m_dwBytes, &lpRetData->m_dwFlags, ( sockaddr * )&lpRetData->m_addr, &lpRetData->m_nAddrLen, ( WSAOVERLAPPED * )&lpRetData->m_ov_ex, 0 );
            if( SOCKET_ERROR == ret )
            {
                DWORD dwErr = WSAGetLastError();
                if( WSA_IO_PENDING != dwErr )
                {
                    cout << "WSARecvFrom error = " << dwErr << endl;
                    nRet = 1;
                }
            }
        }

        if( 0 != nRet )
        {
            lpMsg->set_bool( 0, false );
            PostQueuedCompletionStatus( m_pAioImpl->m_hIocp, 0, 0, ( WSAOVERLAPPED * )&lpRetData->m_ov_ex );
        }

        return nRet;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[4] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::udp_send_ret( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        using namespace std;
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;
        aio_key * lpAioKey = ( aio_key * )lpMsg->get_void( 1, 0 );
        xos_box::i_buf * lpBuf = lpMsg->get_buf( 0, 0 );

        --lpAioKey->m_nRequestNum;

        if( ( 0 != nResult ) || ( 0 == dwBytes ) || !lpMsg->get_bool( 0, 0 ) )
        {
            nRet = 1;
        }

        if( 0 == nRet )
        {
            lpMsg->set_bool( 0, true );
            lpMsg->set_int( 4, dwBytes );
        }
        else
        {
            lpMsg->set_bool( 0, false );
        }

        m_pAioImpl->notify( lpMsg );

        return nRet;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_nData[2] : peer port
    // m_szStr[2] : peer ip
    // 
    // m_nData[4] : send len
    // m_lpBuf[0] : send buf
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::udp_send( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        using namespace std;
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;
        void * lpUserKey = lpMsg->get_void( 0, 0 );
        aio_key * lpAioKey = ( aio_key * )lpMsg->get_void( 1, 0 );
        xos::i_socket * lpSocket = lpAioKey->m_pSocket;
        SOCKET hSocket = ( SOCKET )lpSocket->get_socket();
        xos_box::i_buf * lpBuf = lpMsg->get_buf( 0, 0 );

        // 
        // if i write code like this, it will crash : varible around szIp is corrupt. After ONE day debugging, i do not known why.
        // 
        // resovled 2012-06-19 13:35
        // 
        aio_data * lpRetData = 0;
        
        if( 0 == nRet )
        {
            lpMsg->set_int( 0, i_aio::AIO_UDP_SEND_RET );
            lpMsg->set_bool( 0, false );
            lpAioKey->m_nRequestNum++;
        }

        if( 0 == nRet )
        {
            lpRetData = aio_data::get_item_from_pool( true );
            lpRetData->init();
            lpRetData->m_nType = lpMsg->get_int( 0, 0 );
            lpRetData->m_lpMsgData = lpMsg;
        }

        if( 0 == nRet )
        {
            char szIp[100] = { 0 };
            DWORD dwIp = 0;
            nRet = lpSocket->get_ip_by_name( lpMsg->get_str( 2, 0, 0, 0 ), &dwIp, szIp );
            if( 0 == nRet )
            {
                lpRetData->m_addr.sin_port = htons( lpMsg->get_int( 2, 0 ) );
                lpRetData->m_addr.sin_addr.s_addr = dwIp;
                lpRetData->m_addr.sin_family = AF_INET;
            }
        }

        if( 0 == nRet )
        {
            WSABUF * lpWsaBuf = lpBuf->get_wsa_buf( 0 );
            lpMsg->set_bool( 0, true );
            int ret = ::WSASendTo( hSocket, lpWsaBuf, 1, &lpRetData->m_dwBytes, lpRetData->m_dwFlags, 
                ( const sockaddr * )&lpRetData->m_addr, 
                sizeof( sockaddr_in ), 
                ( WSAOVERLAPPED * )&lpRetData->m_ov_ex, 
                0 );
            if( SOCKET_ERROR == ret )
            {
                DWORD dwErr = WSAGetLastError();
                if( WSA_IO_PENDING != dwErr )
                {
                    cout << "WSASendTo error = " << dwErr << endl;
                    nRet = 1;
                }
            }
        }

        if( 0 != nRet )
        {
            lpMsg->set_bool( 0, false );
            PostQueuedCompletionStatus( m_pAioImpl->m_hIocp, 0, 0, ( WSAOVERLAPPED * )&lpRetData->m_ov_ex );
        }

        return nRet;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::shut_down_ret( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;

        aio_key * lpAioKey = (aio_key *)lpMsg->get_void( 1, 0 );
        xos::i_socket * lpSocket = lpAioKey->m_pSocket;
        int how = lpMsg->get_int( 1, 0 );

        lpSocket->shut_down( how );

        m_pAioImpl->notify( lpMsg );
        lpMsg = 0;

        return nRet;
    }

    // 
    // m_nData[0] : cmd type
    // m_nData[1] : how
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::shut_down( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;

        aio_key * lpAioKey = (aio_key *)lpMsg->get_void( 1, 0 );
        xos::i_socket * lpSocket = lpAioKey->m_pSocket;
        int how = lpMsg->get_int( 1, 0 );

        lpMsg->set_int( 0, i_aio::AIO_UDP_SHUTDOWN_RET );
        lpMsg->set_bool( 0, true );
        lpAioKey->m_eState = aio_key::KEY_STATE_CLOSING;

        CancelIo( (HANDLE)lpSocket->get_socket() );

        {
            aio_helper obj( m_pAioImpl );
            obj.post_msg( lpMsg );
            lpMsg = 0;
        }

        return nRet;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::close_net_ret( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;
        aio_key * lpAioKey = (aio_key *)lpMsg->get_void( 1, 0 );

        if( 0 == lpAioKey->m_nRequestNum )
        {
            lpMsg->set_void( 1, 0 );
            m_pAioImpl->notify( lpMsg );
            lpMsg = 0;
            lpAioKey->release();
            lpAioKey = 0;
        }
        else
        {
            aio_helper obj( m_pAioImpl );
            obj.post_msg( lpMsg );
            lpMsg = 0;
        }

        return nRet;
    }

    // 
    // m_bData[0] : true or false for opt result
    // 
    // m_nData[0] : cmd type
    // m_nData[1] : linger.l_onoff
    // m_nData[2] : linger.l_linger
    // 
    // m_lpData[0] : UserKey
    // m_lpData[1] : AioKey
    // 
    int aio_udp::close_net( int nResult, DWORD dwBytes, aio_key * lpKey, aio_data * lpData )
    {
        int nRet = 0;

        xos_box::i_msg * lpMsg = lpData->m_lpMsgData;

        aio_key * lpAioKey = ( aio_key * )lpMsg->get_void( 1, 0 );
        int onoff = lpMsg->get_int( 1, 0 );
        int linger = lpMsg->get_int( 2, 0 );
        xos::i_socket * lpSocket = lpAioKey->m_pSocket;

        lpMsg->set_int( 0, i_aio::AIO_UDP_CLOSE_RET );
        lpMsg->set_bool( 0, true );
        lpAioKey->m_eState = aio_key::KEY_STATE_CLOSING;

        lpSocket->close( onoff, linger );

        {
            aio_helper obj( m_pAioImpl );
            obj.post_msg( lpMsg );
            lpMsg = 0;
        }

        return nRet;
    }

} // xos_aio
